/**
 * 使用说明:
 * 通过new操作符初始化WxStore的state,mutations
 * 然后通过getter属性获取存储在store中的值
 * 通过commit去提交数据并执行用户自己设置的mutationcd gis中对象的函数属性
 * 带有'_'前缀的属性是建议用户自己使用的,虽然用户仍然可以调用
 * _store: 存储着状态管理器状态的对象
 */
class WxStore {
  /**
   * @param {object} state 用户初始化的一个store,state中不包含的任何属性都不会被运行存入_store
   * 这是为了让用户只用通过查看他初始化的state就能知道store中到底有哪些数据
   * @param {array} _types 存储着mutations的所有属性名
   * @param {object} mutations
   */
  constructor({state, mutations, getters}) {
    const targetType = {
      state: {
        type: 'object',
        name: 'state',
      },
      mutations: {
        type: 'object',
        name: 'mutations',
      },
      mutations: {
        type: 'object',
        name: 'getters',
      },
    };
    typeDetect({state, mutations, getters}, targetType);
    this._store = state;
    this._state = state;
    this._mutations = this.map(mutations, {});
    this._getters = this.mapGetter(getters, {});
    this.getters = this._getters;
  }
  /**
   * 用以更新store的值
   * @param {*} type
   * @param {*} value
   */
  _setter(type, value) {
    const _state = this._state;
    if (_state[type] === undefined) {
      console.error(`This data does not exist in state. 
      Please initialize it in state.`);
    } else {
      const _store = this._store;
      _store[type] = value;
    }
  }
  /**
   * 根据接收的type找到指定的_mutations,然后进行运算
   * 结果存入store中
   * @param {*} type 用户要访问的mutations类型
   * @param {*} payload 用户传输的负载
   * @return {*}
   */
  commit(type, payload) {
    const _type = type;
    const _mutations = this._mutations;
    if (_mutations[_type] === undefined) {
      console.error(`This data does not exist in mutations. 
      Please initialize it in mutations.`);
    } else {
      const _store = this._store;
      const _result = _mutations[_type](_store, payload);
      return _result;
    }
  }

  /**
   * 将initVal映射到另一个对象上
   * @param {object} initVal
   * @param {object} target
   * @return {array}
   */
  map(initVal, target) {
    const _keys = Object.keys(initVal);
    _keys.forEach((key) => {
      target[key] = initVal[key];
    });
    return initVal;
  }

  /**
   * 通过代理,将用户传递的gtters对象中的函数属性,绑定到getters对象中的属性上
   * @param {object} initVal
   * @param {object} target
   * @return {array}
   */
  mapGetter(initVal, target) {
    const _store = this._store;
    const _keys = Object.keys(initVal);
    _keys.forEach((key) => {
      // const handler = {
      //   get(trapTarget, proxyKey) {
      //     return initVal[key].call(null, _store);
      //   },
      // };
      // target[key] = new Proxy({}, handler);

      Object.defineProperty(target, key, {
        get: function() {
          return initVal[key].call(null, _store);
        },
      });
    });

    return target;
  }
}

/**
 * 用以检查类型是否与targetType一致
 * @interface {
 * 1:{type:string, name:string},
 * 2:{type:string, name:string}
 * ...
 * n:{type:string, name:string}
 * } ITargetType
 * type是与name名称相同的属性所应该具有的值.
 * name是要检查的属性
 * @param {object} detectType
 * @param {ITargetType} targetType
 * @param {boolean} isTest 为真就不打印错误信息,用于减少单元测试的信息干扰
 */
function typeDetect(detectType, targetType, isTest = false) {
  const keys = Object.keys(targetType);
  let _result = true;
  keys.forEach((key) => {
    const name = targetType[key].name;
    if (typeof detectType[name] !== targetType[key].type) {
      if (!isTest) {
        console.error(`${detectType[name]} type error.`);
        console.error(`name is ${name}.`);
      }
      _result = false;
    }
  });
  return _result;
}

module.exports.WxStore = WxStore;
module.exports.typeDetect = typeDetect;
